import { EventEmitter } from 'events';

/**
 * Mock RabbitMQ Connection
 */
export class MockConnection extends EventEmitter {
  public isConnected = true;
  public channel: MockChannel;

  constructor() {
    super();
    this.channel = new MockChannel();
  }

  async close(): Promise<void> {
    this.isConnected = false;
    this.emit('close');
  }

  async createChannel(): Promise<MockChannel> {
    return new MockChannel();
  }
}

/**
 * Mock RabbitMQ Channel
 */
export class MockChannel extends EventEmitter {
  public isOpen = true;
  private queues = new Map<string, MockQueue>();
  private exchanges = new Map<string, MockExchange>();
  private consumers = new Map<string, MockConsumer>();

  async assertQueue(
    queue: string,
    options?: any
  ): Promise<{ queue: string; messageCount: number; consumerCount: number }> {
    if (!this.queues.has(queue)) {
      this.queues.set(queue, new MockQueue(queue));
    }
    const mockQueue = this.queues.get(queue)!;
    return {
      queue,
      messageCount: mockQueue.messages.length,
      consumerCount: mockQueue.consumers.size,
    };
  }

  async assertExchange(exchange: string, type: string, options?: any): Promise<void> {
    this.exchanges.set(exchange, new MockExchange(exchange, type));
  }

  async bindQueue(queue: string, exchange: string, routingKey: string): Promise<void> {
    const mockQueue = this.queues.get(queue);
    const mockExchange = this.exchanges.get(exchange);
    if (mockQueue && mockExchange) {
      mockExchange.bindings.set(routingKey, queue);
    }
  }

  async publish(
    exchange: string,
    routingKey: string,
    content: Buffer,
    options?: any
  ): Promise<boolean> {
    const mockExchange = this.exchanges.get(exchange);
    if (mockExchange) {
      const boundQueue = mockExchange.bindings.get(routingKey);
      if (boundQueue) {
        const queue = this.queues.get(boundQueue);
        if (queue) {
          queue.addMessage({
            content,
            properties: options || {},
            fields: { routingKey, exchange },
          });
        }
      }
    }
    return true;
  }

  async sendToQueue(queue: string, content: Buffer, options?: any): Promise<boolean> {
    const mockQueue = this.queues.get(queue);
    if (mockQueue) {
      mockQueue.addMessage({
        content,
        properties: options || {},
        fields: { routingKey: queue },
      });
    }
    return true;
  }

  async consume(
    queue: string,
    onMessage: (msg: any) => void,
    options?: any
  ): Promise<{ consumerTag: string }> {
    const consumerTag = `consumer_${Date.now()}_${Math.random().toString(36).substring(2)}`;
    const consumer = new MockConsumer(consumerTag, onMessage);
    
    this.consumers.set(consumerTag, consumer);
    
    const mockQueue = this.queues.get(queue);
    if (mockQueue) {
      mockQueue.addConsumer(consumer);
    }

    return { consumerTag };
  }

  async cancel(consumerTag: string): Promise<void> {
    const consumer = this.consumers.get(consumerTag);
    if (consumer) {
      this.consumers.delete(consumerTag);
      // Remove from all queues
      for (const queue of this.queues.values()) {
        queue.removeConsumer(consumer);
      }
    }
  }

  async ack(message: any): Promise<void> {
    // Mock acknowledgment
  }

  async nack(message: any, allUpTo?: boolean, requeue?: boolean): Promise<void> {
    // Mock negative acknowledgment
  }

  async deleteQueue(queue: string): Promise<{ messageCount: number }> {
    const mockQueue = this.queues.get(queue);
    const messageCount = mockQueue ? mockQueue.messages.length : 0;
    this.queues.delete(queue);
    return { messageCount };
  }

  async deleteExchange(exchange: string): Promise<void> {
    this.exchanges.delete(exchange);
  }

  async close(): Promise<void> {
    this.isOpen = false;
    this.emit('close');
  }

  // Helper methods for testing
  getQueue(name: string): MockQueue | undefined {
    return this.queues.get(name);
  }

  getExchange(name: string): MockExchange | undefined {
    return this.exchanges.get(name);
  }

  getAllQueues(): Map<string, MockQueue> {
    return new Map(this.queues);
  }
}

/**
 * Mock Queue
 */
export class MockQueue {
  public messages: MockMessage[] = [];
  public consumers = new Set<MockConsumer>();

  constructor(public name: string) {}

  addMessage(message: MockMessage): void {
    this.messages.push(message);
    this.processMessages();
  }

  addConsumer(consumer: MockConsumer): void {
    this.consumers.add(consumer);
    this.processMessages();
  }

  removeConsumer(consumer: MockConsumer): void {
    this.consumers.delete(consumer);
  }

  private processMessages(): void {
    // Process messages asynchronously to simulate real behavior
    setImmediate(() => {
      while (this.messages.length > 0 && this.consumers.size > 0) {
        const message = this.messages.shift();
        const consumer = Array.from(this.consumers)[0]; // Round-robin simulation
        
        if (message && consumer) {
          consumer.handleMessage({
            content: message.content,
            properties: message.properties,
            fields: message.fields,
          });
        }
      }
    });
  }
}

/**
 * Mock Exchange
 */
export class MockExchange {
  public bindings = new Map<string, string>(); // routingKey -> queueName

  constructor(public name: string, public type: string) {}
}

/**
 * Mock Consumer
 */
export class MockConsumer {
  constructor(
    public tag: string,
    private onMessage: (msg: any) => void
  ) {}

  handleMessage(message: any): void {
    // Simulate async message handling
    setImmediate(() => {
      this.onMessage(message);
    });
  }
}

/**
 * Mock Message
 */
export interface MockMessage {
  content: Buffer;
  properties: any;
  fields: any;
}

/**
 * Mock amqplib module
 */
export const mockAmqplib = {
  connect: jest.fn().mockResolvedValue(new MockConnection()),
  Connection: MockConnection,
  Channel: MockChannel,
};

/**
 * Helper function to create mock client
 */
export const createMockClient = () => {
  const connection = new MockConnection();
  return {
    connection,
    channel: connection.channel,
    isConnected: () => connection.isConnected,
    isReady: () => connection.isConnected,
    close: () => connection.close(),
    getConnectionStats: () => ({
      totalConnections: 1,
      availableConnections: 1,
      busyConnections: 0,
      poolSize: 5,
    }),
    getConnectionPoolStats: () => ({
      totalConnections: 1,
      availableConnections: 1,
      busyConnections: 0,
      poolSize: 5,
    }),
    getConfig: () => ({
      host: 'localhost',
      port: 5672,
      username: 'guest',
      password: 'guest',
      virtualHost: '/',
    }),
    createWorkQueue: (queueName: string) => {
      const mockQueue: any = {
        queueName,
        _handler: null as Function | null,
        _handlers: [] as Function[],
        _workerCount: 0,
        _isRunning: false,
      };

      // 添加方法
      mockQueue.setupQueue = jest.fn().mockResolvedValue(undefined);
      mockQueue.startWorkers = jest.fn().mockImplementation(async (handler: Function, workerCount: number = 1, options: any = {}) => {
        // 模拟工作者处理消息 - 支持多次调用添加更多workers
        if (!mockQueue._handlers) {
          mockQueue._handlers = [];
        }

        // 添加新的handlers
        for (let i = 0; i < workerCount; i++) {
          mockQueue._handlers.push(handler);
        }

        mockQueue._workerCount = mockQueue._handlers.length;
        mockQueue._isRunning = true;
      });
      mockQueue.stopWorkers = jest.fn().mockImplementation(async () => {
        mockQueue._isRunning = false;
      });
      mockQueue.sendTask = jest.fn().mockImplementation(async (task: any) => {
        // 模拟任务处理 - 随机选择一个handler
        if (mockQueue._handlers && mockQueue._handlers.length > 0 && mockQueue._isRunning) {
          const randomHandler = mockQueue._handlers[Math.floor(Math.random() * mockQueue._handlers.length)];
          setTimeout(async () => {
            try {
              // 检查handler是否是FunctionHandler对象
              if (randomHandler && typeof randomHandler.handle === 'function') {
                // 创建一个模拟的Message对象
                const mockMessage = { body: task, id: `mock_${Date.now()}` };
                await randomHandler.handle(mockMessage);
              } else if (typeof randomHandler === 'function') {
                // 直接调用函数
                await randomHandler(task);
              }
            } catch (error) {
              console.warn('Mock handler error:', error);
            }
          }, 10);
        }
      });
      mockQueue.sendTasks = jest.fn().mockImplementation(async (tasks: any[]) => {
        // 模拟批量任务处理 - 分发给不同的handlers
        if (mockQueue._handlers && mockQueue._handlers.length > 0 && mockQueue._isRunning) {
          tasks.forEach((task, index) => {
            const handler = mockQueue._handlers[index % mockQueue._handlers.length];
            setTimeout(async () => {
              try {
                // 检查handler是否是FunctionHandler对象
                if (handler && typeof handler.handle === 'function') {
                  // 创建一个模拟的Message对象
                  const mockMessage = { body: task, id: `mock_${Date.now()}_${index}` };
                  await handler.handle(mockMessage);
                } else if (typeof handler === 'function') {
                  // 直接调用函数
                  await handler(task);
                }
              } catch (error) {
                console.warn('Mock handler error:', error);
              }
            }, 10 * (index + 1));
          });
        }
      });
      mockQueue.sendBatch = jest.fn().mockResolvedValue([true, true, true]);
      mockQueue.deleteQueue = jest.fn().mockResolvedValue({ messageCount: 0 });
      mockQueue.purgeQueue = jest.fn().mockResolvedValue(0);
      mockQueue.getQueueStats = jest.fn().mockResolvedValue({
        messageCount: 0,
        consumerCount: mockQueue._workerCount || 0,
      });
      mockQueue.getStats = jest.fn().mockImplementation(() => ({
        queueName: mockQueue.queueName,
        workerCount: mockQueue._workerCount || 0,
        isRunning: mockQueue._isRunning || false,
      }));
      mockQueue.close = jest.fn().mockResolvedValue(undefined);

      return mockQueue;
    },
    createPubSub: (exchangeName: string) => {
      const subscribers: Map<string, Function[]> = new Map();
      return {
        publish: jest.fn().mockImplementation(async (message: any, options: any = {}) => {
          const routingKey = options.routingKey || '';

          // 如果没有指定routingKey，广播给所有订阅者
          if (!routingKey) {
            subscribers.forEach((handlers) => {
              handlers.forEach(handler => {
                setTimeout(async () => {
                  try {
                    if (handler && typeof (handler as any).handle === 'function') {
                      const mockMessage = { body: message, id: `mock_${Date.now()}` };
                      await (handler as any).handle(mockMessage);
                    } else if (typeof handler === 'function') {
                      await handler(message);
                    }
                  } catch (error) {
                    console.warn('Mock PubSub handler error:', error);
                  }
                }, 10);
              });
            });
          } else {
            // 有routingKey时，进行匹配
            subscribers.forEach((handlers, subscriberKey) => {
              // 简单的路由键匹配逻辑
              const isMatch = routingKey === subscriberKey ||
                             subscriberKey === '#' || // 匹配所有
                             (subscriberKey.endsWith('*') && routingKey.startsWith(subscriberKey.slice(0, -1))) ||
                             (subscriberKey.includes('*') && new RegExp(subscriberKey.replace(/\*/g, '[^.]*')).test(routingKey));

              if (isMatch) {
                handlers.forEach(handler => {
                  setTimeout(async () => {
                    try {
                      if (handler && typeof (handler as any).handle === 'function') {
                        const mockMessage = { body: message, id: `mock_${Date.now()}` };
                        await (handler as any).handle(mockMessage);
                      } else if (typeof handler === 'function') {
                        await handler(message);
                      }
                    } catch (error) {
                      console.warn('Mock PubSub handler error:', error);
                    }
                  }, 10);
                });
              }
            });
          }
        }),
        subscribe: jest.fn().mockImplementation(async (handler: Function, options: any = {}) => {
          const routingKey = options.routingKey || options.queueName || 'default';
          if (!subscribers.has(routingKey)) {
            subscribers.set(routingKey, []);
          }
          subscribers.get(routingKey)!.push(handler);
        }),
        setupExchange: jest.fn().mockImplementation(async (options: any = {}) => {
          // Mock exchange setup - store exchange type for routing logic
        }),
        stopAllSubscribers: jest.fn().mockResolvedValue(undefined),
        unsubscribe: jest.fn().mockResolvedValue(undefined),
        close: jest.fn().mockResolvedValue(undefined),
      };
    },
    createRPC: (queueName: string) => {
      const rpcHandlers: Map<string, Function> = new Map();
      return {
        setupServer: jest.fn().mockImplementation(async (handlers: Record<string, Function>) => {
          Object.entries(handlers).forEach(([method, handler]) => {
            rpcHandlers.set(method, handler);
          });
        }),
        setupClient: jest.fn().mockResolvedValue(undefined),
        call: jest.fn().mockImplementation(async (method: string, params: any, options: any = {}) => {
          const handler = rpcHandlers.get(method);
          if (handler) {
            // 模拟超时
            if (options.timeout && params.delay && params.delay > options.timeout) {
              throw new Error(`RPC call timeout after ${options.timeout}ms`);
            }

            // 模拟延迟
            if (params.delay) {
              await new Promise(resolve => setTimeout(resolve, Math.min(params.delay, 100))); // 限制最大延迟
            }

            return await handler(params);
          }
          return { result: 'mock-result' };
        }),
        close: jest.fn().mockResolvedValue(undefined),
      };
    },
    createRouting: (exchangeName: string) => {
      const bindings: Map<string, Function> = new Map();
      return {
        setupExchange: jest.fn().mockResolvedValue(undefined),
        bindQueue: jest.fn().mockImplementation(async (queueName: string, routingKey: string, handler: Function) => {
          bindings.set(`${queueName}:${routingKey}`, handler);
        }),
        publish: jest.fn().mockImplementation(async (routingKey: string, message: any) => {
          // 模拟路由消息
          bindings.forEach((handler, key) => {
            const [, bindingKey] = key.split(':');
            if (bindingKey === routingKey) {
              setTimeout(() => handler(message), 10);
            }
          });
        }),
        send: jest.fn().mockImplementation(async (message: any, routingKey: string) => {
          // 模拟发送消息 - 与publish相同的逻辑
          bindings.forEach((handler, key) => {
            const [, bindingKey] = key.split(':');
            if (bindingKey === routingKey) {
              setTimeout(async () => {
                try {
                  if (handler && typeof (handler as any).handle === 'function') {
                    const mockMessage = { body: message, id: `mock_${Date.now()}` };
                    await (handler as any).handle(mockMessage);
                  } else if (typeof handler === 'function') {
                    await handler(message);
                  }
                } catch (error) {
                  console.warn('Mock routing handler error:', error);
                }
              }, 10);
            }
          });
        }),
        subscribe: jest.fn().mockResolvedValue(undefined),
        close: jest.fn().mockResolvedValue(undefined),
      };
    },
    createProducer: () => ({
      initialize: jest.fn().mockResolvedValue(undefined),
      send: jest.fn().mockResolvedValue(undefined),
      close: jest.fn().mockResolvedValue(undefined),
    }),
    getMiddlewarePipeline: () => ({
      // Mock middleware pipeline
    }),
    connectionPool: {
      getConnection: jest.fn().mockResolvedValue(connection),
      returnConnection: jest.fn(),
    },
  };
};
